﻿/*
 Copyright (c) 2011-2012 Aaron Zhang
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
*/

using KBCruiser.Configuration;
using KBCruiser.RequestHandlers;
using mshtml;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace KBCruiser.Controls
{
    public partial class ReferencePane : UserControl
    {
        private Collection<SearcherConfig> searchers = new Collection<SearcherConfig>();
        private Stopwatch stopwatch = new Stopwatch();
        public delegate void UrlChangedEventHandler(object sender, UrlChangedEventArgs e);
        public event UrlChangedEventHandler UrlChanged;
        private bool triggerHighlight = false;

        public ListView LvResults
        {
            get { return lvResults; }
        }

        public WebBrowser WbContent
        {
            get { return wbContent; }
        }

        public ReferencePane()
        {
            InitializeComponent();
            RegisterEvents();
        }

        private void RegisterEvents()
        {
            lvResults.SelectedIndexChanged += new EventHandler(lvResults_SelectedIndexChanged);
            lvRelated.SelectedIndexChanged += new EventHandler(lvRelated_SelectedIndexChanged);
            lvResults.DoubleClick += new EventHandler(lvResults_DoubleClick);
            txtUrl.KeyDown += txtUrl_KeyDown;
            wbContent.DocumentCompleted += wbContent_DocumentCompleted;
            wbContent.StatusTextChanged += wbContent_StatusTextChanged;
        }

        void wbContent_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (triggerHighlight)
            {
                IHTMLDocument2 doc2 = wbContent.Document.DomDocument as IHTMLDocument2;
                string result = doc2.body.outerHTML;
                string substitution = null;
                string pattern = null;
                foreach (string key in keywords)
                {
                    pattern = string.Format(@"(>[^<]*?)(\b{0}\b)",key);
                    substitution = "$1<span style='background-color: rgb(255, 255, 0);'>$2</span>";
                    result = Regex.Replace(result, pattern, substitution, RegexOptions.IgnoreCase);
                }
                doc2.body.innerHTML = result;
                triggerHighlight = false;
            }
        }

        void txtUrl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter)
            {
                if (txtUrl.Text.StartsWith("http", StringComparison.InvariantCultureIgnoreCase))
                {
                    wbContent.Navigate(txtUrl.Text);
                }
                else
                {
                    ProcessStartInfo info = new ProcessStartInfo();
                    string input = txtUrl.Text.Trim();
                    if (input.Contains(" "))
                    {
                        int index = input.IndexOf(' ');
                        info.FileName = input.Substring(0, index);
                        info.Arguments = input.Substring(index + 1);
                    }
                    else
                    {
                        info.FileName = input;
                    }
                    Process process = Process.Start(info);
                }
            }
        }

        void lvResults_DoubleClick(object sender, EventArgs e)
        {
            if (lvResults.SelectedItems.Count > 0)
            {
                Process.Start("iexplore.exe", lvResults.SelectedItems[0].SubItems[2].Text);
            }
        }

        void lvResults_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lvResults.SelectedItems.Count > 0)
            {
                triggerHighlight = true;
                wbContent.Navigate(lvResults.SelectedItems[0].SubItems[2].Text);
                txtUrl.Text = lvResults.SelectedItems[0].SubItems[2].Text;
            }
        }

        void lvRelated_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lvRelated.SelectedItems.Count > 0)
            {
                Search(lvRelated.SelectedItems[0].SubItems[0].Text, false);
                UrlChanged(this, new UrlChangedEventArgs(lvRelated.SelectedItems[0].SubItems[0].Text));
            }
        }

        void wbContent_StatusTextChanged(object sender, EventArgs e)
        {
            browserStatus.Text = wbContent.StatusText;
        }

        void cb_Click(object sender, EventArgs e)
        {
            CheckBox cb = sender as CheckBox;
            if (cb != null)
            {
                SearcherConfig searcher = cb.Tag as SearcherConfig;
                if (searcher != null)
                {
                    searcher.Default = cb.Checked;
                }
            }
        }

        private void btnCollapse_Click(object sender, EventArgs e)
        {
            if (btnCollapse.Text == "<")
            {
                splitContainer.SplitterDistance = 0;
                splitContainer.Panel1.Hide();
                splitContainer.Left = 0;
                splitContainer.Width += 15;
                btnCollapse.Text = ">";
            }
            else
            {
                splitContainer.SplitterDistance = 350;
                splitContainer.Panel1.Show();
                splitContainer.Left = 15;
                splitContainer.Width -= 15;
                btnCollapse.Text = "<";
            }
        }

        public void AddSearcher(SearcherConfig searcher)
        {
            CheckBox cb = new CheckBox();
            int controlCount = pnlCheckboxContainer.Controls.Count;
            if (controlCount > 0)
            {
                cb.Left = pnlCheckboxContainer.Controls[controlCount - 1].Right;
            }
            cb.Name = searcher.Name;
            cb.Checked = searcher.Default;
            cb.Text = searcher.Name;
            cb.AutoSize = true;
            cb.Tag = searcher;
            cb.Click += new EventHandler(cb_Click);
            pnlCheckboxContainer.Controls.Add(cb);
            searchers.Add(searcher);
        }

        public void ClearCheckBox()
        {
            foreach (Control ctrl in pnlCheckboxContainer.Controls)
            {
                CheckBox checkBox = ctrl as CheckBox;
                if (checkBox != null)
                {
                    checkBox.Checked = false;
                }
            }
        }

        private string[] keywords = null;

        public void Search(string target)
        {
            Search(target, false);
        }

        private void Search(string target, bool searchHint)
        {
            stopwatch.Reset();
            stopwatch.Start();
            toolStripStatus.Text = "Processing...";
            lvResults.Items.Clear();
            foreach (SearcherConfig searcher in searchers)
            {
                if (searcher.Default)
                {
                    HttpRequestHandler handler = new HttpRequestHandler(this, searcher);
                    handler.SendRequest(target);
                    keywords = target.Split(' ');
                }
            }

            if (searchHint)
            {
                lvRelated.Items.Clear();
                HintRequestHandler hintHandler = new HintRequestHandler(this);
                hintHandler.SendRequest(target);
            }
        }

        delegate void BindResultInvoker(Collection<Reference> answerItems);
        delegate void BindHintInvoker(Collection<string> hints);


        public void BindResult(Collection<Reference> answerItems)
        {
            if (InvokeRequired)
            {
                this.Invoke(new BindResultInvoker(BindResult), answerItems);
            }
            else
            {
                foreach (Reference item in answerItems)
                {
                    ListViewItem listViewItem = new ListViewItem();
                    listViewItem.SubItems[0].Text = item.Source;
                    listViewItem.SubItems.Add(item.Title);
                    listViewItem.SubItems.Add(item.Url);
                    lvResults.Items.Add(listViewItem);
                }
                toolStripStatus.Text = string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds);
            }
        }

        public void BindHints(Collection<string> hints)
        {
            if (InvokeRequired)
            {
                this.Invoke(new BindHintInvoker(BindHints), hints);
            }
            else
            {
                foreach (string hint in hints)
                {
                    ListViewItem listViewItem = new ListViewItem();
                    listViewItem.SubItems[0].Text = hint;
                    lvRelated.Items.Add(hint);
                }
            }
        }
    }

    public class UrlChangedEventArgs : EventArgs
    {
        private string url;
        public UrlChangedEventArgs(string url)
        {
            this.url = url;
        }

        public string Url
        {
            get { return url; }
            set { url = value; }
        }
    }
}
